/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "InstanceScript.h"
#include "stratholme.h"
#include "Player.h"

const Position BlackGuardPos[10] = 
{
	{4032.73f+0.0f, -3378.26f+0.0f, 119.76f, 4.67f},
	{4032.73f+2.0f, -3378.26f+2.0f, 119.76f, 4.67f},
	{4032.73f+2.0f, -3378.26f-2.0f, 119.76f, 4.67f},
	{4032.73f-2.0f, -3378.26f+2.0f, 119.76f, 4.67f},
	{4032.73f-2.0f, -3378.26f-2.0f, 119.76f, 4.67f},
	
	{4032.73f+0.0f, -3407.38f+0.0f, 115.56f, 0.0f},
	{4032.73f+2.0f, -3407.38f+2.0f, 115.56f, 0.0f},
	{4032.73f+2.0f, -3407.38f-2.0f, 115.56f, 0.0f},
	{4032.73f-2.0f, -3407.38f+2.0f, 115.56f, 0.0f},
	{4032.73f-2.0f, -3407.38f-2.0f, 115.56f, 0.0f}
};

class instance_stratholme : public InstanceMapScript
{
    public:
        instance_stratholme() : InstanceMapScript("instance_stratholme", 329) { }

        struct instance_stratholme_InstanceMapScript : public InstanceScript
        {
            instance_stratholme_InstanceMapScript(Map* map) : InstanceScript(map)
            {
            }

            void Initialize()
            {
				_baronRunProgress = 0;
				_baronRunTime = 0;
				_zigguratState1 = 0;
				_zigguratState2 = 0;
				_zigguratState3 = 0;
				_slaughterProgress = 0;
				_slaughterNPCs = 0;
				_postboxesOpened = 0;

				_zigguratDoorsGUID1 = 0;
				_zigguratDoorsGUID2 = 0;
				_zigguratDoorsGUID3 = 0;
				_zigguratDoorsGUID4 = 0;
				_zigguratDoorsGUID5 = 0;
				_gauntletGateGUID = 0;
				_slaughterGateGUID = 0;
				_baronRivendareGUID = 0;

				events.Reset();
            }

			void OnPlayerEnter(Player* player)
			{
				if (_baronRunTime > 0)
					if (Aura* aura = player->AddAura(SPELL_BARON_ULTIMATUM, player))
						aura->SetDuration(_baronRunTime*MINUTE*IN_MILLISECONDS);
			}

            void OnCreatureCreate(Creature* creature)
            {
                switch (creature->GetEntry())
                {
                    case NPC_BARON_RIVENDARE:
                        _baronRivendareGUID = creature->GetGUID();
                        break;
					case NPC_VENOM_BELCHER:
					case NPC_BILE_SPEWER:
						if (_slaughterProgress == 0)
							++_slaughterNPCs;
						break;
					case NPC_RAMSTEIN_THE_GORGER:
						if (_slaughterProgress == 1)
							++_slaughterNPCs;
						break;
					case NPC_MINDLESS_UNDEAD:
						if (_slaughterProgress == 2)
							++_slaughterNPCs;
						break;
					case NPC_BLACK_GUARD:
						if (_slaughterProgress == 3)
							++_slaughterNPCs;
						break;
                }
            }

			void ProcessSlaughterEvent()
			{
				if (_slaughterProgress == 1)
				{
					if (Creature* baron = instance->GetCreature(_baronRivendareGUID))
						baron->AI()->Talk(SAY_BRAON_SUMMON_RAMSTEIN);

					Position pos = {4032.20f, -3378.06f, 119.75f, 4.67f};
					instance->SummonCreature(NPC_RAMSTEIN_THE_GORGER, pos);
				}
				if (_slaughterProgress == 2)
				{
					for (uint32 i = 0; i < 33; ++i)
						events.ScheduleEvent(EVENT_SPAWN_MINDLESS, 5000 + i*210);
					if (Creature* baron = instance->GetCreature(_baronRivendareGUID))
						if (GameObject* gate = baron->FindNearestGameObject(GO_SLAUGHTER_GATE_SIDE, 200.0f))
							gate->SetGoState(GO_STATE_ACTIVE);
				}
				if (_slaughterProgress == 3)
				{
					events.ScheduleEvent(EVENT_SPAWN_BLACK_GUARD, 20000);
				}
				if (_slaughterProgress == 4)
				{
					if (Creature* baron = instance->GetCreature(_baronRivendareGUID))
						baron->AI()->Talk(SAY_BARON_GUARD_DEAD);
					if (GameObject* gate = instance->GetGameObject(_zigguratDoorsGUID5))
						gate->SetGoState(GO_STATE_ACTIVE);

				}
			}

			void OnUnitDeath(Unit* unit)
			{
				switch (unit->GetEntry())
				{
					case NPC_VENOM_BELCHER:
					case NPC_BILE_SPEWER:
					case NPC_RAMSTEIN_THE_GORGER:
					case NPC_MINDLESS_UNDEAD:
					case NPC_BLACK_GUARD:
						if (--_slaughterNPCs == 0)
						{
							++_slaughterProgress;
							ProcessSlaughterEvent();
							SaveToDB();
						}
						break;
				}
			}

            void OnGameObjectCreate(GameObject* go)
            {
                switch (go->GetEntry())
                {
                    case GO_ZIGGURAT_DOORS1:
                        _zigguratDoorsGUID1 = go->GetGUID();
                        if (GetData(TYPE_ZIGGURAT1) >= 1)
                            go->SetGoState(GO_STATE_ACTIVE);
                        break;
                    case GO_ZIGGURAT_DOORS2:
                        _zigguratDoorsGUID2 = go->GetGUID();
                        if (GetData(TYPE_ZIGGURAT2) >= 1)
                            go->SetGoState(GO_STATE_ACTIVE);
                        break;
                    case GO_ZIGGURAT_DOORS3:
                        _zigguratDoorsGUID3 = go->GetGUID();
                        if (GetData(TYPE_ZIGGURAT3) >= 1)
                            go->SetGoState(GO_STATE_ACTIVE);
                        break;
                    case GO_GAUNTLET_GATE:
                        _gauntletGateGUID = go->GetGUID();
                        if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
                            go->SetGoState(GO_STATE_ACTIVE);
                        break;
                    case GO_SLAUGTHER_GATE:
                        _slaughterGateGUID = go->GetGUID();
                        if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
                            go->SetGoState(GO_STATE_ACTIVE);
                        break;
                    case GO_ZIGGURAT_DOORS4:
                        _zigguratDoorsGUID4 = go->GetGUID();
                        if (_slaughterProgress == 4)
                            go->SetGoState(GO_STATE_ACTIVE);
                        break;
                    case GO_ZIGGURAT_DOORS5:
                        _zigguratDoorsGUID5 = go->GetGUID();
                        if (_slaughterProgress == 4)
                            go->SetGoState(GO_STATE_ACTIVE);
                        break;
					case GO_SLAUGHTER_GATE_SIDE:
						if (_slaughterProgress >= 2)
                            go->SetGoState(GO_STATE_ACTIVE);
						break;
                }
            }

			void CheckZiggurats()
			{
				if (_zigguratState1 == 2 && _zigguratState2 == 2 && _zigguratState3 == 2)
				{
					instance->LoadGrid(4035.83f, -3336.31f);
					if (Creature* baron = instance->GetCreature(_baronRivendareGUID))
						baron->AI()->Talk(SAY_BRAON_ZIGGURAT_FALL_YELL);

					if (GameObject* gate = instance->GetGameObject(_gauntletGateGUID))
						gate->SetGoState(GO_STATE_ACTIVE);
					if (GameObject* gate = instance->GetGameObject(_slaughterGateGUID))
						gate->SetGoState(GO_STATE_ACTIVE);
				}
			}

            void SetData(uint32 type, uint32 data)
            {
				switch (type)
				{
					case TYPE_BARON_RUN:
					{
						if (_baronRunProgress == DATA_BARON_RUN_NONE)
						{
							_baronRunProgress = DATA_BARON_RUN_GATE;
							_baronRunTime = 45;
							DoCastSpellOnPlayers(SPELL_BARON_ULTIMATUM);
							events.ScheduleEvent(EVENT_BARON_TIME, 60000);

							instance->LoadGrid(4035.83f, -3336.31f);
							if (Creature* baron = instance->GetCreature(_baronRivendareGUID))
								baron->AI()->Talk(SAY_BARON_INIT_YELL);
						}
						break;
					}
					case TYPE_ZIGGURAT1:
					{
						if (data == _zigguratState1+1)
							++_zigguratState1;

						if (_zigguratState1 == 1)
							if (GameObject* ziggurat = instance->GetGameObject(_zigguratDoorsGUID1))
								ziggurat->SetGoState(GO_STATE_ACTIVE);

						CheckZiggurats();
						break;
					}
					case TYPE_ZIGGURAT2:
					{
						if (data == _zigguratState2+1)
							++_zigguratState2;

						if (_zigguratState2 == 1)
							if (GameObject* ziggurat = instance->GetGameObject(_zigguratDoorsGUID2))
								ziggurat->SetGoState(GO_STATE_ACTIVE);

						CheckZiggurats();
						break;						
					}
					case TYPE_ZIGGURAT3:
					{
						if (data == _zigguratState3+1)
							++_zigguratState3;

						if (_zigguratState3 == 1)
							if (GameObject* ziggurat = instance->GetGameObject(_zigguratDoorsGUID3))
								ziggurat->SetGoState(GO_STATE_ACTIVE);

						CheckZiggurats();
						break;
					}
					case TYPE_BARON_FIGHT:
					{
						if (GameObject* gate = instance->GetGameObject(_zigguratDoorsGUID5))
							gate->SetGoState(data == IN_PROGRESS ? GO_STATE_READY : GO_STATE_ACTIVE);
						return;
					}
					case TYPE_MALLOW:
						++_postboxesOpened;
						break;
				}

                SaveToDB();
            }

            std::string GetSaveData()
            {
                std::ostringstream saveStream;
				saveStream << "S T " << _baronRunProgress << ' ' << _baronRunTime << ' ' << _zigguratState1 << ' ' << _zigguratState2 << ' ' << _zigguratState3 << ' ' << _slaughterProgress << ' ' << _postboxesOpened;
                return saveStream.str();
            }

            void Load(const char* in)
            {
				if (!in)
					return;

				char dataHead1, dataHead2;
				std::istringstream loadStream(in);
				loadStream >> dataHead1 >> dataHead2;
				if (dataHead1 == 'S' && dataHead2 == 'T')
				{
					loadStream >> _baronRunProgress;
					loadStream >> _baronRunTime;
					loadStream >> _zigguratState1;
					loadStream >> _zigguratState2;
					loadStream >> _zigguratState3;
					loadStream >> _slaughterProgress;
					loadStream >> _postboxesOpened;
				}

				if (_baronRunTime > 0)
					events.ScheduleEvent(EVENT_BARON_TIME, 60000);

				if (_slaughterProgress > 0 && _slaughterProgress < 4)
					events.ScheduleEvent(EVENT_FORCE_SLAUGHTER_EVENT, 5000);
            }

            uint32 GetData(uint32 type) const
            {
				switch (type)
                {
					case TYPE_ZIGGURAT1:
						return _zigguratState1;
					case TYPE_ZIGGURAT2:
						return _zigguratState2;
					case TYPE_ZIGGURAT3:
						return _zigguratState3;
					case TYPE_MALLOW:
						return _postboxesOpened;
                }
                return 0;
            }

            void Update(uint32 diff)
            {
                events.Update(diff);
				switch (events.ExecuteEvent())
                {
                    case EVENT_BARON_TIME:
					{
						--_baronRunTime;
						instance->LoadGrid(4035.83f, -3336.31f);
						Creature* baron = instance->GetCreature(_baronRivendareGUID);
						if (baron && !baron->IsInCombat())
						{
							switch (_baronRunTime)
							{
								case 10:
									baron->AI()->Talk(SAY_BARON_10M);
									break;
								case 5:
									baron->AI()->Talk(SAY_BARON_5M);
									if (Creature* ysida = baron->FindNearestCreature(NPC_YSIDA, 50.0f))
										ysida->AI()->SetData(1, 1);
									break;
								case 0:
									baron->AI()->Talk(SAY_BARON_0M);
									DoRemoveAurasDueToSpellOnPlayers(SPELL_BARON_ULTIMATUM);
									break;
							}
						}

						if (_baronRunTime > 0)
							events.ScheduleEvent(EVENT_BARON_TIME, 60000);
						else
							events.ScheduleEvent(EVENT_EXECUTE_PRISONER, 0);

						SaveToDB();
						break;
					}
					case EVENT_EXECUTE_PRISONER:
					{
						instance->LoadGrid(4035.83f, -3336.31f);
						Creature* baron = instance->GetCreature(_baronRivendareGUID);
						if (baron && baron->IsAlive())
						{
							if (!baron->IsInCombat())
							{
								baron->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK1H);
								if (Creature* ysida = baron->FindNearestCreature(NPC_YSIDA, 50.0f))
									Unit::Kill(baron, ysida);
							}
							else
								events.ScheduleEvent(EVENT_EXECUTE_PRISONER, 1000);
						}
						break;
					}
                    case EVENT_SPAWN_MINDLESS:
					{
						Position pos = {3941.75f, -3393.06f, 119.70f, 0.0f};
						instance->SummonCreature(NPC_MINDLESS_UNDEAD, pos);
						break;
					}
					case EVENT_FORCE_SLAUGHTER_EVENT:
					{
						Map::PlayerList const& PlayerList = instance->GetPlayers();
						if (!PlayerList.isEmpty())
							for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
								if (Player* player = i->GetSource())
									if (player->GetDistance2d(4034.97f, -3402.13f) < 50.0f)
									{
										ProcessSlaughterEvent();
										return;
									}
							
						events.ScheduleEvent(EVENT_FORCE_SLAUGHTER_EVENT, 3000);
						break;
					}
					case EVENT_SPAWN_BLACK_GUARD:
					{
						for (uint8 i = 0; i < 5; ++i)
							if (Creature* guard = instance->SummonCreature(NPC_BLACK_GUARD, BlackGuardPos[i]))
							{
								guard->SetWalk(true);
								guard->GetMotionMaster()->MovePoint(0, BlackGuardPos[i+5]);
								guard->SetHomePosition(BlackGuardPos[i+5]);
								if (i == 0 && guard->AI())
									guard->AI()->Talk(SAY_BLACK_GUARD_INIT);
							}

						if (GameObject* gate = instance->GetGameObject(_zigguratDoorsGUID4))
							gate->SetGoState(GO_STATE_ACTIVE);
						break;
					}
                }
            }

		private:
			uint32 _baronRunProgress;
			uint32 _baronRunTime;
			uint32 _zigguratState1;
			uint32 _zigguratState2;
			uint32 _zigguratState3;
			uint32 _slaughterProgress;
			uint32 _slaughterNPCs;
			uint32 _postboxesOpened;
			EventMap events;

			uint64 _zigguratDoorsGUID1;
			uint64 _zigguratDoorsGUID2;
			uint64 _zigguratDoorsGUID3;
			uint64 _zigguratDoorsGUID4;
			uint64 _zigguratDoorsGUID5;
			uint64 _slaughterGateGUID;
			uint64 _gauntletGateGUID;
			uint64 _baronRivendareGUID;
        };

        InstanceScript* GetInstanceScript(InstanceMap* map) const
        {
            return new instance_stratholme_InstanceMapScript(map);
        }
};

void AddSC_instance_stratholme()
{
    new instance_stratholme();
}